package com.rlyy.basic.resolver.exception;

import com.rlyy.common.enums.CommonEnum;
import com.rlyy.common.response.Result;
import com.rlyy.common.response.ResultUtil;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;

import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 初始化统一异常处理映射关系
 * 统一处理业务抛出的异常
 *
 * @author yuanqinglong
 * @since 2020/11/30 9:28
 */
@ToString
@EqualsAndHashCode
@Component
public class MyExceptionHandlerExceptionResolver implements ApplicationContextAware, InitializingBean {

    static final Logger logger = LoggerFactory.getLogger(MyExceptionHandlerExceptionResolver.class);

    /**
     * spring上下文
     */
    private ApplicationContext applicationContext;

    /**
     * 缓存统一异常处理映射
     */
    private final Map<ControllerAdviceBean, MyExceptionHandlerMethodResolver> exceptionHandlerAdviceCache =
            new LinkedHashMap<>();

    @Override
    public void afterPropertiesSet() throws Exception {
        this.initExceptionHandlerAdviceCache();
    }

    /**
     * 初始化统一异常映射关系
     */
    private void initExceptionHandlerAdviceCache() {
        if (getApplicationContext() == null) {
            return;
        }
        // 找到所有@ControllerAdvice和@RestControllerAdvice注解的bean
        List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
        // 遍历定义的统一异常处理类
        for (ControllerAdviceBean adviceBean : adviceBeans) {
            // 获取这个统一异常处理类中所有标注@ExceptionHandler注解的方法
            MyExceptionHandlerMethodResolver resolver = new MyExceptionHandlerMethodResolver(adviceBean.getBeanType());
            // 如果定义了异常处理方法 添加到异常处理缓存中，key为异常处理类
            if (resolver.hasExceptionMappings()) {
                this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
            }
        }
    }

    /**
     * 调用异常处理目标方法
     *
     * @param exception 抛出的异常
     * @return 返回异常处理方法封装的结果
     */
    public Result invokeExceptionMethod(Throwable exception) {
        // 获取处理此异常的异常处理bean和对应方法
        HandlerMethod exceptionHandlerMethod = this.getExceptionHandlerMethod(exception);
        try {
            // 调用执行统一异常处理方法 返回异常处理方法封装的数据
            return (Result) exceptionHandlerMethod.getMethod().invoke(exceptionHandlerMethod.getBean(), exception);
        } catch (Exception e) {
            logger.error("统一异常处理错误: ", e.getCause());
            // 调用默认的异常处理器
            return ResultUtil.error(CommonEnum.SERVER_IS_BUSY);
        }
    }


    /**
     * 获取异常处理类和方法
     *
     * @param exception 抛出的异常信息
     * @return 返回对应的异常处理类和方法
     */
    protected HandlerMethod getExceptionHandlerMethod(Throwable exception) {
        // 遍历初始化标注了@ControllerAdvice和@RestControllerAdvice的缓存映射器
        for (Map.Entry<ControllerAdviceBean, MyExceptionHandlerMethodResolver> entry : this.exceptionHandlerAdviceCache.entrySet()) {
            ControllerAdviceBean controllerAdvice = entry.getKey();
            // 异常和处理方法的映射关系
            MyExceptionHandlerMethodResolver resolver = entry.getValue();
            // 处理异常要调用的统一异常处理方法
            Method method = resolver.getMappedMethod(exception.getClass());
            if (method != null) {
                return new HandlerMethod(controllerAdvice.resolveBean(), method);
            }
        }
        return null;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    public ApplicationContext getApplicationContext() {
        return this.applicationContext;
    }


}
